package com.weihe.toolkit.ftp4j;

import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@Getter
@Setter
public class FTPToolkit {
	
	private static final Log log = LogFactory.getLog(FTPToolkit.class);
	
	private FTPClient client;

	public FTPToolkit(String host, int port, String username, String password) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		client = new FTPClient();
		client.setCharset("UTF-8");
		client.setType(FTPClient.TYPE_AUTO);
		try {
			client.connect(host, port);
			client.login(username, password);
			log.info("Ftp server listening on address " + host);
		} catch (Exception e) {
			client = null;
			throw new FTPRuntimeException(e);
		}
	}

	public void upload(File localfile, String remoteFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
		FtpListener listener = FtpListener.instance(FTPOptType.UP);
		String cud = client.currentDirectory();
		try {
			client.changeDirectory(remoteFolderPath);
			if (!localfile.exists())
				throw new FTPRuntimeException(localfile.getPath() + " not exist!");
			if (!localfile.isFile())
				throw new FTPRuntimeException(localfile.getPath() + " is not a file!");
			if (listener != null)
				client.upload(localfile, listener);
			else
				client.upload(localfile);
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}finally{
			client.changeDirectory(cud);
		}
	}
	public void compareAndMergeDifferentFiles(String localFolder, String remoteFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException{
		if(StringUtils.isNotBlank(localFolder)&&StringUtils.isNotBlank(remoteFolderPath)){
//			if(localFolder.endsWith("")){
//				
//			}
			remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
			File file = new File(localFolder);
			File[] files = file.listFiles();		
			FTPFile[] ftps = list(remoteFolderPath);
			if(null != files && null != ftps){
				for(File f : files){
					int i;
					for(i=0;i<ftps.length;i++){
						if(f.isFile() && StringUtils.equals(f.getName(), ftps[i].getName()) && f.lastModified()==ftps[i].getModifiedDate().getTime()){
							break;
						}
					}
					if(i>=ftps.length){
						if(f.isFile()){
							this.upload(f, remoteFolderPath);
						}else if(f.isDirectory()){
							try {
								this.mkdirs(remoteFolderPath+PathToolkit.FILE_SEPARATOR+f.getName());
							} catch (Exception e) {
								e.printStackTrace();
								log.error("mkdires error:"+remoteFolderPath+PathToolkit.FILE_SEPARATOR+f.getName());
							}
							this.compareAndMergeDifferentFiles(localFolder+PathToolkit.FILE_SEPARATOR+f.getName(), remoteFolderPath+PathToolkit.FILE_SEPARATOR+f.getName());
						}
					}
				}
				for(FTPFile ftp : ftps){
					int i;
					for(i=0;i<files.length;i++){
						if(StringUtils.equals(ftp.getName(),files[i].getName()) && 
						((ftp.getType()==FTPFile.TYPE_DIRECTORY && files[i].isDirectory()) || (ftp.getType()==FTPFile.TYPE_FILE && files[i].isFile()))){
							break;
						}
					}
					if(i>=files.length){
						if(!StringUtils.equals(ftp.getName(), ".") && !StringUtils.equals(ftp.getName(), "..")){
							if(ftp.getType() == FTPFile.TYPE_FILE){
								this.deleteFile(remoteFolderPath+PathToolkit.FILE_SEPARATOR+ftp.getName());
							}else if(ftp.getType() == FTPFile.TYPE_DIRECTORY){
								this.deleteDirectory(remoteFolderPath+PathToolkit.FILE_SEPARATOR+ftp.getName());
							}
						}
					}
				}
			}
		}
	}

	public void upload(String localfilepath,String remoteFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		File localfile = new File(localfilepath);
		upload(localfile, remoteFolderPath);
	}  

	public void uploadListPath(List<String> localFilePaths, String remoteFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		if(null == localFilePaths || localFilePaths.size() <= 0 || StringUtils.isBlank(remoteFolderPath)){
			return;
		}
		String cud = client.currentDirectory();
		remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
		try {
			client.changeDirectory(remoteFolderPath);
			FtpListener listener = FtpListener.instance(FTPOptType.UP);
			for (String path : localFilePaths) {
				File file = new File(path);
				if (!file.exists())
					throw new FTPRuntimeException(path + " not exist!");
				if (!file.isFile())
					throw new FTPRuntimeException(path
							+ "is not a file!");
				if (listener != null)
					client.upload(file, listener);
				else
					client.upload(file);
			}
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}finally{
			client.changeDirectory(cud);
		}
	}

	public void uploadListFile(List<File> localFiles,String remoteFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		String cud = client.currentDirectory();
		try {
			client.changeDirectory(remoteFolderPath);
			remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
			FtpListener listener = FtpListener.instance(FTPOptType.UP);
			for (File file : localFiles) {
				if (!file.exists())
					throw new FTPRuntimeException(file.getPath()
							+ " not exist!");
				if (!file.isFile())
					throw new FTPRuntimeException(file.getPath()
							+ " is a file!");
				if (listener != null)
					client.upload(file, listener);
				else
					client.upload(file);
			}
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}finally{
			client.changeDirectory(cud);
		}
	}
	
	public void download(String remoteFileName, String localFolderPath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		boolean x = isExist(remoteFileName);
		FtpListener listener = FtpListener.instance(FTPOptType.UP);
		File localFolder = new File(localFolderPath);
		if (localFolder.isFile()) {
			throw new FTPRuntimeException("local file is not a directory!");
		} else {
			if (!localFolder.exists())
				localFolder.mkdirs();
		}
		if (x) {
			String localfilepath = PathToolkit.formatPath4File(localFolderPath
					+ File.separator + new File(remoteFileName).getName());
			try {
				if (listener != null)
					client.download(remoteFileName, new File(localfilepath),
							listener);
				else
					client.download(remoteFileName, new File(localfilepath));
			} catch (Exception e) {
				throw new FTPRuntimeException(e);
			}
		} else {			
			throw new FTPRuntimeException();
		}
	}
	
	public String[] listFiles(String dir) throws Exception {
		String cud = client.currentDirectory();
		try {
			client.changeDirectory(dir);
			String[] values = client.listNames();
			if (values != null) {
				// 将文件排序(忽略大小写)
				Arrays.sort(values, new Comparator<String>(){
					public int compare(String val1, String val2) {
						return val1.compareToIgnoreCase(val2);
					}
				});
			}
			return values;
		} catch(FTPException fe) {
			// 忽略文件夹不存在的情况
			String mark = "code=550";
			if (fe.toString().indexOf(mark) == -1) {
				throw fe;
			}
		}finally{
			this.client.changeDirectory(cud);
		}
		return new String[0];
	}
	
	public FTPFile[] list(String dir){
		try {
			return client.list(dir);
		} catch(Exception fe) {
			// 忽略文件夹不存在的情况
			fe.printStackTrace();
		}
		return null;
	}

	public boolean isExist(String remotePath) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException {
		return getFileType(remotePath) != -1;
	}
	
	private int getFileType(String dir) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException{
		int retValue = -1;
		String cud = client.currentDirectory();
		if(StringUtils.isNotBlank(dir)){
			dir = PathToolkit.formatPath4FTP(dir);
			FTPFile[] files = null;  
			try {  
				files = client.list(dir);  
			} catch (Exception e) {  
				return -1;  
			}  
			if (files.length > 1) {  
				return FTPFile.TYPE_DIRECTORY;  
			} else if (files.length == 1) {  
				FTPFile f = files[0];  
				if (f.getType() == FTPFile.TYPE_DIRECTORY) {  
					return FTPFile.TYPE_DIRECTORY;  
				}  
				String path = dir + PathToolkit.FILE_SEPARATOR + f.getName();  
				try {  
					int len = client.list(path).length;  
					if (len == 1) {  
						return FTPFile.TYPE_DIRECTORY;  
					} else {  
						return FTPFile.TYPE_FILE;  
					}  
				} catch (Exception e) {  
					return FTPFile.TYPE_FILE;  
				}  
			} else {  
				try {  
					client.changeDirectory(dir);  
					client.changeDirectoryUp();  
					return FTPFile.TYPE_DIRECTORY;  
				} catch (Exception e) {  
					return -1;  
				}  
			}  
			
		}
		client.changeDirectory(cud);
		return retValue;
    }  
	
	public boolean mkdirs(String directory) throws Exception {  
		boolean retValue = false;
		String cud = client.currentDirectory();
        if (StringUtils.isNotBlank(directory)) {  
        	directory = PathToolkit.formatPath4FTP(directory);
        	String[] dirs = directory.split(PathToolkit.FILE_SEPARATOR);  
        	for (int i = 0; i < dirs.length; i++) {  
        		String dir = dirs[i];  
        		if (!StringUtils.isEmpty(dir)) {  
        			if (!isExist(dir)) {  
        				client.createDirectory(dir);  
        			}  
        			client.changeDirectory(dir);  
        		}  
        	}  
        	retValue = true;
        }
        client.changeDirectory(cud);
        return retValue;
    } 
	
	public Date getFileModifyDate(String filePath){
		try {
			return client.modifiedDate(filePath);
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FTPIllegalReplyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FTPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public void deleteFile(String file){
		if(StringUtils.isNotBlank(file)){
			try {
				this.client.deleteFile(file);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	public void deleteDirectory(String directory){
		if(StringUtils.isNotBlank(directory)){
			try {
				FTPFile[] files = this.list(directory);
				if(null != files && files.length > 0){
					for(FTPFile f : files){
						if(f.getType() == FTPFile.TYPE_FILE){
							this.deleteFile(directory+PathToolkit.FILE_SEPARATOR+f.getName());
						}else if(f.getType() == FTPFile.TYPE_DIRECTORY){
							if(!StringUtils.equals(f.getName(), ".") && !StringUtils.equals(f.getName(), "..")){
								deleteDirectory(directory+PathToolkit.FILE_SEPARATOR+f.getName());
							}
						}
					}
				}
				this.client.deleteDirectory(directory);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public boolean closeConnection() {
		if (client == null)
			return true;
		if (client.isConnected()) {
			try {
				client.disconnect(true);
				return true;
			} catch (Exception e) {
				try {
					client.disconnect(false);
				} catch (Exception e1) {
					e1.printStackTrace();
					return false;
				}
			}
		}
		return true;
	}
}